package com.smart.redis.zset;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.smart.redis.client.RedisClient;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.Tuple;

/**
 * 每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序
 * 有序集合的成员是唯一的,但分数(score)却可以重复
 * 
* @ClassName: Redis2ZSet 
* @Description: TODO redis zset有序集合操作 
* @Company:e-smart.top
* @author Thinkpad 
* @version 1.0 2018年8月30日 下午1:53:18
 */
public class Redis2ZSet {

	/**
	 * 
	* 如果某个 value 已经是有序集的成员，那么更新这个 value 的 score 值，并通过重新插入这个 value 元素，来保证该 value 在正确的位置上
	* 如果 key 不存在，则创建一个空的有序集并执行 ZADD 操作
	* 如果key对应的数据不是有序集合，程序报错
	* 
	* @Title: zadd 
	* @Description: TODO  有序集合添加或更新数据
	* @param @param key 键
	* @param @param value 元素
	* @param @param score 分数-排序标准参数（整数值或双精度浮点数）
	* @param @return    被成功添加的新成员的数量，不包括那些被更新的、已经存在的成员 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zadd(String key, String value, double score){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zadd = jedis.zadd(key, score, value);
			return zadd;
		} catch (Exception e) {
			// TODO: zadd 
			System.err.println("zadd : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 如果某个 value 已经是有序集的成员，那么更新这个 value 的 score 值，并通过重新插入这个 value 元素，来保证该 value 在正确的位置上
	* 如果 key 不存在，则创建一个空的有序集并执行 ZADD 操作
	* 如果key对应的数据不是有序集合，程序报错
	*
	* @Title: zadd 
	* @Description: TODO 有序集合批量添加或更新数据
	* @param @param key
	* @param @param scoreMembers 元素和分数键值对集合
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zadd(String key, Map<String,Double> scoreMembers){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zadd = jedis.zadd(key, scoreMembers);
			return zadd;
		} catch (Exception e) {
			// TODO: zadd 
			System.err.println("zadd : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zcard 
	* @Description: TODO 获取有序集合中元素数量
	* @param @param key
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zcard(String key){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zcard = jedis.zcard(key);
			return zcard;
		} catch (Exception e) {
			// TODO: zcard 
			System.err.println("zcard : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zcount 
	* @Description: TODO 获取有序集合中一定分数排序范围内的集合数据
	* @param @param key
	* @param @param min 最小分数（包含）
	* @param @param max 最大分数（包含）
	* @param @return     元素数量
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zcount(String key,double min,double max){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zcard = jedis.zcount(key, min, max);
			return zcard;
		} catch (Exception e) {
			// TODO: zcard 
			System.err.println("zcard : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 当 key 不存在，或 member 不是 key 的成员时， ZINCRBY key increment member 等同于 ZADD key increment member
	* 当 key 不是有序集类型时，程序报错
	* 
	* @Title: zincrby 
	* @Description: TODO 为有序集合中元素的分数增加一定数据 
	* @param @param key 键
	* @param @param member 集合中的元素
	* @param @param increment 增量数值（整数值或双精度浮点数-可正可负）
	* @param @return    设定文件 
	* @return Double    返回类型 
	* @throws
	 */
	public static Double zincrby(String key,String member,double increment){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Double zincrby = jedis.zincrby(key, increment, member);
			return zincrby;
		} catch (Exception e) {
			// TODO: zincrby 
			System.err.println("zincrby : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zrange 
	* @Description: TODO 获取指定分数（score）区间内的元素集合 
	* @param @param key 键
	* @param @param start 起始位置
	* @param @param end 结束位置
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrange(String key,long start,long end){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrange = jedis.zrange(key, start, end);
			return zrange;
		} catch (Exception e) {
			// TODO: zrange 
			System.err.println("zrange : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zrangeByScore 
	* @Description: TODO  获取所有 分数score 值介于 min 和 max 之间(包括等于 min 或 max )的集合元素
	* @param @param key 键
	* @param @param min 最小值
	* @param @param max 最大值
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrangeByScore(String key,double min,double max){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrangeByScore = jedis.zrangeByScore(key, min, max);
			return zrangeByScore;
		} catch (Exception e) {
			// TODO: zrangeByScore 
			System.err.println("zrangeByScore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* count,offset参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count )，
	* 注意当 offset 很大时，定位 offset 的操作可能需要遍历整个有序集，此过程最坏复杂度为 O(N) 时间。
	* 
	* @Title: zrangeByScore 
	* @Description: TODO  指定返回数量和区间，获取所有 分数score 值介于 min 和 max 之间(包括等于 min 或 max )的集合元素
	* @param @param key 键
	* @param @param min 最小分数
	* @param @param max 最大分数
	* @param @param offset 页码
	* @param @param count 返回条数
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrangeByScore(String key,double min,double max,int offset,int count){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrangeByScore = jedis.zrangeByScore(key, min, max, offset, count);
			return zrangeByScore;
		} catch (Exception e) {
			// TODO: zrangeByScore 
			System.err.println("zrangeByScore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 有序集成员按 score 值递增(从小到大)顺序排列
	* 排名以 0 为底，也就是说， score 值最小的成员排名为 0
	* 
	* @Title: zrank 
	* @Description: TODO 获取有序集 key 中成员 member 的排名 （递增排名）
	* @param @param key
	* @param @param member
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zrank(String key,String member){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zrank = jedis.zrank(key, member);
			return zrank;
		} catch (Exception e) {
			// TODO: zrank 
			System.err.println("zrank : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 当 key 存在但不是有序集类型时，会报错
	* 如果元素不存在，不进行操作
	* 
	* @Title: zrem 
	* @Description: TODO 移除有序集合中的一个或多个元素 
	* @param @param key
	* @param @param members
	* @param @return     操作成功的元素数量
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zrem(String key,String... members){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zrem = jedis.zrem(key, members);
			return zrem;
		} catch (Exception e) {
			// TODO: zrem 
			System.err.println("zrem : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 区间分别以下标参数 start 和 stop 指出，包含 start 和 stop 在内
	* 注意是排名不是分数，是分数从小到大的排名
	* 
	* @Title: zremrangeByRank 
	* @Description: TODO 移除有序集合中指定 排名 区间的元素 
	* @param @param key
	* @param @param start 开始索引-索引从0开始
	* @param @param end 结束索引
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zremrangeByRank(String key,long start,long end){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zremrangeByRank = jedis.zremrangeByRank(key, start, end);
			return zremrangeByRank;
		} catch (Exception e) {
			// TODO: zremrangeByRank 
			System.err.println("zremrangeByRank : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zremrangeByScore 
	* @Description: TODO 删除有序集合中一定分数score 区间的元素
	* @param @param key
	* @param @param start 分数起始位置
	* @param @param end 分数结束位置
	* @param @return    被移除元素的数量
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zremrangeByScore(String key,double start,double end){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zremrangeByScore = jedis.zremrangeByScore(key, start, end);
			return zremrangeByScore;
		} catch (Exception e) {
			// TODO: zremrangeByScore 
			System.err.println("zremrangeByScore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 其中成员的位置按 score 值递减(从大到小)来排列，与ZRANGE相反
	* 
	* @Title: zrevrange 
	* @Description: TODO 获取指定排名区间内的元素集合 
	* @param @param key
	* @param @param start 开始位置
	* @param @param end 结束位置
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrevrange(String key,long start,long end){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrevrange = jedis.zrevrange(key, start, end);
			return zrevrange;
		} catch (Exception e) {
			// TODO: zrevrange 
			System.err.println("zrevrange : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 返回的有序集成员按 score 值递减(从大到小)的次序排列。
	* 
	* @Title: zrevrangeByScore 
	* @Description: TODO 获取一定分数score范围 内的元素（默认包括等于 max 或 min ）
	* @param @param key 键
	* @param @param max 分数最大值
	* @param @param min 分数最小值
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrevrangeByScore(String key,double max,double min){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrevrangeByScore = jedis.zrevrangeByScore(key, max, min);
			return zrevrangeByScore;
		} catch (Exception e) {
			// TODO: zrevrangeByScore 
			System.err.println("zrevrangeByScore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* 返回的有序集成员按 score 值递减(从大到小)的次序排列。
	* 
	* @Title: zrevrangeByScore 
	* @Description: TODO  指定返回数量和区间，获取所有 分数score 值介于 max 和 min 之间(包括等于 min 或 max )的集合元素
	* @param @param key
	* @param @param max 分数最大值
	* @param @param min 分数最小值
	* @param @param offset 页码
	* @param @param count 每页数量
	* @param @return    设定文件 
	* @return Set<String>    返回类型 
	* @throws
	 */
	public static Set<String> zrevrangeByScore(String key,double max,double min,int offset,int count){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Set<String> zrevrangeByScore = jedis.zrevrangeByScore(key, max, min, offset, count);
			return zrevrangeByScore;
		} catch (Exception e) {
			// TODO: zrevrangeByScore 
			System.err.println("zrevrangeByScore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zrevrank 
	* @Description: TODO 获取元素的排名 
	* @param @param key
	* @param @param member 元素值
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zrevrank(String key,String member){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zrevrank = jedis.zrevrank(key, member);
			return zrevrank;
		} catch (Exception e) {
			// TODO: zrevrank 
			System.err.println("zrevrank : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zscore 
	* @Description: TODO 获取元素的分数值 score
	* @param @param key
	* @param @param member
	* @param @return    设定文件 
	* @return Double    返回类型 
	* @throws
	 */
	public static Double zscore(String key,String member){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Double zscore = jedis.zscore(key, member);
			return zscore;
		} catch (Exception e) {
			// TODO: zscore 
			System.err.println("zscore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zunionstore 
	* @Description: TODO 获取多个有序集合的并集并保存到 dstkey有序集合中
	* @param @param dstkey 并集要保存的有序集合-键
	* @param @param keys 要计算并集的有序集合键数组
	* @param @return     保存到 dstkey 的结果集的基数
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zunionstore(String dstkey,String... keys){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zunionstore = jedis.zunionstore(dstkey, keys);
			return zunionstore;
		} catch (Exception e) {
			// TODO: zunionstore 
			System.err.println("zunionstore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	* dstkey 如果存在数据会被覆盖，如果不存在直接创建
	* 
	* @Title: zinterstore 
	* @Description: TODO 计算多个有序集合的交集并保存到另一个有序集合中 （）
	* @param @param dstkey 交集保存的有序集合键
	* @param @param keys 计算交集的有序集合键数组
	* @param @return    设定文件 
	* @return Long    返回类型 
	* @throws
	 */
	public static Long zinterstore(String dstkey,String... keys){
		Jedis jedis = RedisClient.getJedis();
		try {
			
			Long zunionstore = jedis.zinterstore(dstkey, keys);
			return zunionstore;
		} catch (Exception e) {
			// TODO: zunionstore 
			System.err.println("zunionstore : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	/**
	 * 
	*
	* @Title: zscan 
	* @Description: TODO 查询key下所有匹配**value**的所有有序集合元素 
	* @param @param key 键
	* @param @param value 元素（**value**全匹配）
	* @param @return     返回以元素为键以分数为值的map集合 
	* @return Map<String,Double>    返回类型 
	* @throws
	 */
	public static Map<String,Double> zscan(String key,String value ){
		Jedis jedis = RedisClient.getJedis();
		try {
			String cursor = "0";
			Map<String,Double> values = new HashMap<String,Double>();
    		ScanParams sp = new ScanParams();
    		sp.match("*"+value+"*");//匹配规则pattern
    		sp.count(100);//每次查询数量
    		//迭代查询
    		do{
    			ScanResult<Tuple> zscan = jedis.zscan(key, cursor, sp);
    			//Tuple 存储这元素和分数两部分信息的实体类
                List<Tuple> result = zscan.getResult();
                if(result!=null && result.size() > 0){
                	result.forEach(a->{
                		values.put(a.getElement(), a.getScore());
                	});
                }
                //再处理cursor
                cursor = zscan.getStringCursor();
            }while(!cursor.equals("0"));
    		
			return values;
		} catch (Exception e) {
			// TODO: zscan 
			System.err.println("zscan : "+e.getMessage());
			return null;
		} finally {
			RedisClient.returnToPool(jedis);
		}
	}
	
	public static void main(String[] args) {
		String ll="abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
		System.out.println(ll.length());
	}
}
